﻿Active Record
=============

למרות שה DAO של Yii יכול לטפל בכמעט כל פעולה הקשורה למסד הנתונים, רוב הסיכויים שאנו נבזבז 90% מהזמן שלנו בכתיבת שאילתות SQL אשר מבצעות את הפעולות הנפוצות במסד הנתונים CRUD (יצירה, קריאה, עדכון ומחיקה). גם קשה לנהל את הקוד שאנו כותבים כשהוא מעורבב עם שאילתות SQL שונות. כדי לפתור בעיות אלו אנו יכולים להשתמש ב Active Record.

Active Record או בקיצור AR הינה שיטת ORM (Object-Relational Mapping) נפוצה. כל מחלקת AR מייצגת טבלה במסד הנתונים אשר התכונות שלה מאופיינות כמשתנים במחלקת ה AR, ואובייקט של המחלקה מייצג שורה אחת בטבלה. פעולות CRUD נפוצות מיושמות בתור מתודות במחלקת ה AR. כתוצאה מכך, אנו יכולים לגשת למידע במסד הנתונים בצורה יותר מונחית-עצמים. לדוגמא, אנו יכולים להשתמש בקוד הבא כדי להכניס שורה חדשה בטבלה `tbl_post`:

~~~
[php]
$post=new Post;
$post-»title='כותרת לדוגמא';
$post-»content='תוכן הודעה לדוגמא';
$post-»save();
~~~

במסמך זה אנו נתאר כיצד להגדיר מחלקת AR ולהשתמש בה כדי לבצע פעולות CRUD. בחלק הבא אנו נציג כיצד להשתמש ב AR כדי לטפל בקשרים בין טבלאות במסד הנתונים. לנוחות, אנו נשתמש בטבלה הבאה למטרת הצגת הדוגמאות בחלק זה. הערה: במידה והינך עובד עם מסד נתונים מסוג MySQL, יש להחליף את `AUTOINCREMENT` ב `AUTO_INCREMENT` בשאילתה הבאה.

~~~
[sql]
CREATE TABLE tbl_post (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    title VARCHAR(128) NOT NULL,
    content TEXT NOT NULL,
    create_time INTEGER NOT NULL
);
~~~

» Note|הערה: שימוש ב-AR לא נועד כדי לפתור את כל הבעיות הקשורות למסדי הנתונים. השימוש הטוב ביותר בו נועד על מנת לבצע שאילות SQL פשוטות (CRUD) ושימוש בתור מודל באפליקציה. מומלץ לא להשתמש בו בעת ביצוע שאילתות SQL מורכבות. למטרה זו מומלץ להשתמש ב DAO.


התחברות למסד הנתונים
--------------------------

AR משתמך על חיבור למסד הנתונים כדי לבצע פעולות הקשורות אליו. כברירת מחדל, AR מניח שהרכיב `db` מספק אובייקט [CDbConnection] המאפשר התחברות למסד הנתונים. הגדרות האפליקציה הבאות מציגות דוגמא לשימוש:

~~~
[php]
return array(
    'components'=»array(
        'db'=»array(
            'class'=»'system.db.CDbConnection',
            'connectionString'=»'sqlite:path/to/dbfile',
            // הפעלת מטמון להפחתת עומס
            // 'schemaCachingDuration'=»3600,
        ),
    ),
);
~~~

» Tip|טיפ: מאחר וה AR מסתמך על ה metadata של הטבלאות כדי להחליט לגבי סוגי העמודות בטבלאות, לוקח זמן לקרוא מידע זה אודות הטבלאות ולנתח אותו. במידה ותרשים טבלאות מסדי הנתונים שלך לא משתנה, מומלץ להפעיל את המטמון לגבי הנתונים אודות הטבלאות על ידי הגדרת המאפיין [CDbConnection::schemaCachingDuration] לערך הגדול מ-0.

תמיכה ב AR מוגבלת על פי DBMS. נכון לעכשיו, ה-DBMS הבאים נתמכים:

- [MySQL 4.1 או יותר](http://www.mysql.com)
 
- [PostgreSQL 7.3 או יותר](http://www.postgres.com)

- [Microsoft SQL Server 2000 או יותר](http://www.microsoft.com/sqlserver/)

- [Oracle](http://www.oracle.com)


» Note|הערה: התמיכה ב SQLServer  קיימת מגרסאות 1.0.4 ומעלה. והתמיכה של Oracle קיימת מגרסאות 1.0.5 ומעלה.

אם ברצונך להשתמש ברכיב אפליקציה שהוא לא `db`, או אם ברצונך לעבוד עם כמה מסדי נתונים בעזרת ה AR, רצוי שתדרוס את [CActiveRecord::getDbConnection]. המחלקה [CActiveRecord] הינה מחלקת הבסיס של כל מחלקות ה AR.

» Tip|טיפ: ישנם שני דרכים לעבוד עם כמה מסדי נתונים תוך שימוש ב-AR . אם תרשים מסדי הנתונים הוא שונה, תוכל ליצור מחלקת AR בסיסית שונה אשר בתוכה יהיה צורך לדרוס את [getDbConnection|CActiveRecord::getDbConnection] בצורה שונה. דרך נוספת תיהיה לשנות את המשתנה הסטטי [CActiveRecord::db] בצורה דינאמית בעת הצורך.

הגדרת מחלקת AR
-----------------

כדי לגשת לטבלה במסד הנתונים, אנו קודם צריכים להגדיר מחלקת AR היורשת מ [CActiveRecord]. כל מחלקת AR מייצגת טבלה במסד הנתונים, ואובייקט של המחלקה מייצג רשומה באותה טבלה. הדוגמא הבאה מציגה את הקוד המינימלי הדרוש למחלקת ה AR המייצגת את הטבלה `tbl_post`.

~~~
[php]
class Post extends CActiveRecord
{
    public static function model($className=__CLASS__)
    {
        return parent::model($className);
    }

    public function tableName()
    {
        return 'tbl_post';
    }
}
~~~

» Tip|טיפ: מאחר וניתן לקרוא למחלקות AR במקומות רבים, ניתן לייבא את כל התיקיה המכילה את מחלקת ה AR, במקום לייבא אותם אחד אחד. לדוגמא, אם כל מחלקות ה AR נמצאות תחת התיקיה `protected/models`, ניתן להגדיר את האפליקציה בצורה הבאה:
» ~~~
» [php]
» return array(
»     'import'=»array(
»         'application.models.*',
»     ),
» );
» ~~~

כברירת מחדל, שם מחלקת ה AR הוא זהה לשם של הטבלה במסד הנתונים. יש לדרוס את המתודה [tableName|CActiveRecord::tableName] במידה והם שונים אחד מהשני. המתודה [model|CActiveRecord::model] מוגדרת בצורה הזאת לכל מחלקת AR (הסבר בהמשך).

» Info|מידע: כדי להשתמש באפשרות [קידומת טבלאות](/doc/guide/database.dao#using-table-prefix) הקיימת מגרסאות 1.1.0, המתודה [tableName|CActiveRecord::tableName] של מחלקת AR צריכה להדרס בצורה הבאה,
» ~~~
» [php]
» public function tableName()
» {
»     return '{{post}}';
» }
» ~~~
זאת, במקום להחזיר את שמה המלא של הטבלה, אנו מחזירים את שם הטבלה ללא הקידומת ועוטפים אותה בסוגריים מסולסלות כפולות.

ניתן לגשת לערכים בעמודות של טבלה כמאפיינים של מחלקת ה AR. לדוגמא, הקוד הבא מגדיר את העמודה (מאפיין) `title`:

~~~
[php]
$post=new Post;
$post-»title='הודעה לדוגמא';
~~~

למרות שאנו לא מגדירים ספציפית את המאפיין `title` במחלקת ה `Post`, אנו עדיין יכולים לגשת אליו בקוד המוצג למעלה. הסיבה לכך היא מכיוון ש `title` הינו עמודה בטבלה `tbl_post` , ומחלקת ה AR מאפשרת גישה אליו כמאפיין במחלקה בעזרת פונקצית ה `__get` ב PHP. תזרק שגיאה במידה ויהיה ניסיון לגשת לעמודה אשר לא קיימת בטבלה.

» Info|מידע: במדריך זה, אנו משתמשים באותיות קטנות לכל שמות הטבלאות והעמודות. זאת מאחר וכל DBMS רגיש באופן שונה לאותיות גדולות-קטנות. לדוגמא, PostgreSQL לא רגיש לאותיות גדולות-קטנות לשמות עמודות בטבלאות כברירת מחדל, ואנו חייבים לתחום את שמות העמודות בתנאים בתוך השאילתה במידה ושם העמודה מכיל שילוב של אותיות גדולות וקטנות. שימוש באותיות קטנות בלבד בשמות פותר בעיה זו.

מחלקת AR מסתמכת על מפתחות ראשיים המוגדרים בטבלאות. במידה וטבלה לא מכילה מפתח ראשי, יש צורך בהגדרת המפתח הראשי במחלקה על ידי דריסה של המתודה `primaryKey` כפי שמוצג בדוגמא הבאה,

~~~
[php]
public function primaryKey()
{
    return 'id';
    // בעבור מפתחות מורכבים, יש להחזיר מערך בצורה הבאה
    // return array('pk1', 'pk2');
}
~~~


יצירת רשומה
---------------

בכדי להכניס רשומה חדשה לטבלה במסד הנתונים, אנו יוצרים אובייקט חדש של מחלקת ה AR, מגדירים את המאפיינים שלה בהתאם לעמודות בטבלה, וקוראים למתודה [save|CActiveRecord::save] כדי להשלים את תהליך ההוספה.

~~~
[php]
$post=new Post;
$post-»title='כותרת לדוגמא';
$post-»content='תוכן להודעה לדוגמא';
$post-»create_time=time();
$post-»save();
~~~

במידה והמפתח הראשי של הטבלה הינו ערך מספרי אשר עולה אוטומטית (מוגדר כ auto_increment), לאחר ההוספה לטבלה מחלקת ה AR תכיל מפתח ראשי מעודכן. בדוגמא למעלה, המאפיין `id` משקף את המפתח הראשי של ההודעה החדשה שנוצרה כרגע, למרות שאנו לא משנים אותו בצורה ספציפית.

במידה וישנו ערך ברירת מחדל לעמודה בטבלה (לדוגמא סטרינג או מספר), המאפיין במחלקת ה AR יכיל את אותו ערך ברירת מחדל לאחר יצירת הרשומה החדשה. דרך אחת לשנות ערך ברירת מחדל זה היא על ידי הגדרת המאפיין במחלקת ה AR עם ערך ברירת מחדל חדש:

~~~
[php]
class Post extends CActiveRecord
{
    public $title='אנא הזן כותרת';
    ......
}

$post=new Post;
echo $post-»title;  // זה יציג: אנא הזן כותרת
~~~

החל מגרסא 1.0.2, ניתן להגדיר ערך של [CDbExpression] למאפיין כלשהו לפני שמירת הרשומה (בין אם זה יצירת רשומה חדשה או עדכון רשומה קיימת) במסד הנתונים. לדוגמא, בכדי לשמור את הזמן הנוכחי המוחזר באמצעות הפונקציה `()NOW` ב MySQL, אנו יכולים להשתמש בקוד הבא:

~~~
[php]
$post=new Post;
$post-»create_time=new CDbExpression('NOW()');
// $post-»create_time='NOW()'; לא יעבוד מאחר
// 'NOW()' יהיה כסטרינג ולא כפונקציה
$post-»save();
~~~

» Tip|טיפ: אף על פי ש AR מאפשר לנו לבצע פעולות הקשורות למסד הנתונים ללא צורך בכתיבה של שאילתות SQL מסובכות, אנו בדרך כלל נרצה לדעת אילו שאילתות SQL רצות מאחורי ה AR. ניתן לקבל מידע זה על ידי הפעלת אפשרות [התיעוד](/doc/guide/topics.logging) של Yii. לדוגמא, אנו יכולים להפעיל את [CWebLogRoute] בהגדרות האפליקציה, ואנו נראה את השאילתות שבוצעו בסוף כל עמוד. מגרסא 1.0.5, אנו יכולים להגדיר את המאפיין [CDbConnection::enableParamLogging] לערך השווה ל `true` בהגדרות האפליקציה כדי שהפרמטרים התחומים בשאילתה יוצגו גם הם בתיעוד.

קריאת רשומה
--------------

בכדי לקרוא מידע מטבלה במסד הנתונים, אנו קוראים לאחת ממתודות ה `find` הבאות.

~~~
[php]
// מצא את הרשומה הראשונה המשביע את התנאי שהועבר
$post=Post::model()-»find($condition,$params);
// מצא את הרשומה עם המפתח הראשי שהועבר
$post=Post::model()-»findByPk($postID,$condition,$params);
// מצא את הרשומה עם המאפיין שהועבר
$post=Post::model()-»findByAttributes($attributes,$condition,$params);
// מצא את הרשומה הראשונה עם השאילתה שהועברה
$post=Post::model()-»findBySql($sql,$params);
~~~

בדוגמאות למעלה, אנו קוראים למתודות ה `find` בעזרת `()Post::model`. זכור שהמתודה הסטטית `()model` הינה הכרחית לכל מחלקת AR. המתודה מחזירה אובייקט AR אשר משתמשים בו לגשת למתודות בעלי הרשאה לאותה מחלקה בלבד (משהו דומה למתודות סטטיות במחלקה) באופן מונחה עצמים.

אם המתודה `find` מוצאת רשומה המשביע את התנאים שהועברו, היא תחזיר אובייקט של `Post` כשהמאפיינים שלה מכילים את הערכים של העמודות של אותה רשומה בטבלה. לאחר מכן אנו יכולים לקרוא את הערכים שנטענו בצורה הרגילה בה אנו ניגשים למאפיינים של המחלקה, לדוגמא, `;echo $post-»title`.

מתודת ה `find` תחזיר null במידה ולא נמצא שום דבר במסד הנתונים התואם לתנאים שהועברו.

בעת הקריאה ל `find`, אנו משתמשים ב `condition$` ו  `params$` כדי להגדיר את התנאים של השאילתה. כאן `condition$` יכול להוות סטרינג המייצג את הסעיף `WHERE` בשאילתת SQL, ו `params$` הינו מערך של פרמטרים שערכיהם צריכים להתחם במפתחות שהוגדרו מראש ב `condition$`. לדוגמא,

~~~
[php]
// מצא את הרשומה איפה ש postID = 10
$post=Post::model()-»find('postID=:postID', array(':postID'=»10));
~~~

» Note|הערה: בדוגמא למעלה, אנו נצטרך לבצע חיטוי לייחוס של העמודה `postID` בעבור DBMS מסויימים. לדוגמא, במידה ואנחנו משתמשים ב PostgreSQL, אנו נצטרך לכתוב את התנאי בצורה הבאה `postID"=:postID"`, מאחר ו PostgreSQL כברירת מחדל יתייחס לשמות העמודות ללא רגישות לאותיות גדולות-קטנות.

כמו כן אנו יכולים להשתמש ב `condition$` כדי להגדיר תנאים מורכבים יותר. במקום סטרינג, אנו נותנים ל `condition$` להיות אובייקט של [CDbCriteria], המאפשר לנו להגדיר תנאים נוספים מלבד סעיף ה `WHERE`. לדוגמא,

~~~
[php]
$criteria=new CDbCriteria;
$criteria-»select='title';  // בחר רק את העמודה 'title'
$criteria-»condition='postID=:postID';
$criteria-»params=array(':postID'=»10);
$post=Post::model()-»find($criteria); // $params אינו נחוץ כאן
~~~

זכור, שבעת השימוש ב [CDbCriteria] בתור התנאי של השאילתה, הפרמטר `params$` אינו נחוץ מאחר וניתן להגדיר אותו בעזרת [CDbCriteria], כפי שמוצג למעלה.

כמו כן, במקום שימוש ב [CDbCriteria] ניתן להעביר מערך למתודת ה-`find`.
שמות המפתחות והערכים מתייחסות למאפיינים של התנאים וערכיהם, בהתאם. ניתן לשכתב את הדוגמא למעלה בקוד הבא,

~~~
[php]
$post=Post::model()-»find(array(
    'select'=»'title',
    'condition'=»'postID=:postID',
    'params'=»array(':postID'=»10),
));
~~~

» Info|מידע: כשהתנאי של השאילתה עוסק בהתאמת עמודות והערכים שהוגדרו, אנו יכולים להשתמש ב [findByAttributes()|CActiveRecord::findByAttributes].
אנו נותנים לפרמטר `attributes$` להיות מערך של שמות העמודות בתור המפתחות וערך כל מפתח הינו הערך שאותו אנו רוצים להתאמים בשאילתה. בכמה פריימוורקים (Frameworks), פעולה זו ניתנת לביצוע על ידי קריאה למתודות כמו `findByNameAndTitle`. למרות שגישה זו נראית מושכת, היא גורמת ברוב המקרים לבלבול, קונפליקטים ובעיות של רגישות לאותיות גדולות-קטנות בשמות העמודות.

כשישנם מספר רב של רשומות התואמות לתנאים שהוצבו בשאילתה, אנו יכולים להביא את כולם יחדיו על ידי שימוש במתודות `findAll` הבאות, לכל אחת מהם ישנה מתודת `find` תואמת, כפי שכבר הסברנו.

~~~
[php]
// מצא את כל הרשומות התואמות לתנאי שהועבר
$posts=Post::model()-»findAll($condition,$params);
// מצא את כל הרשומות בעלות המפתח הראשי שהועבר
$posts=Post::model()-»findAllByPk($postIDs,$condition,$params);
// מצא את כל הרשומות התואמות למאפיינים שהועברו
$posts=Post::model()-»findAllByAttributes($attributes,$condition,$params);
// מצא את כל הרשומות אשר תואמות לשאילתה שהועברה
$posts=Post::model()-»findAllBySql($sql,$params);
~~~

במידה ולא נמצאו התאמות לתנאים שהוצבו בשאילתה, `findAll` תחזיר מערך ריק. זה שונה מהערך שיוחזור ממתודות `find` המחזירות null במידה ולא נמצאו רשומות.

מלבד המתודות `find` ו `findAll` המתוארות למעלה, לנוחות השימוש ניתן להשתמש במתודות הבאות גם כן:

~~~
[php]
// קבל את מספר הרשומות התואמות לתנאי שהועבר
$n=Post::model()-»count($condition,$params);
// קבל את מספר הרשומות התואמות לשאילתה שהועברה
$n=Post::model()-»countBySql($sql,$params);
// בדוק אם קיימת לפחות רשומה אחת התואמת לתנאי שהועבר
$exists=Post::model()-»exists($condition,$params);
~~~

עדכון רשומה
---------------

לאחר שאובייקט AR עם ערכים לעמודות, ניתן לשנותם ולשמור אותם בחזרה לטבלה במסד.

~~~
[php]
$post=Post::model()-»findByPk(10);
$post-»title='כותרת חדש';
$post-»save(); // שמור את השינויים במסד
~~~

כפי שניתן לראות, אנו משתמשים באותה מתודה [save()|CActiveRecord::save] כדי לבצע פעולות הוספה ועדכון. במידה ואובייקט ה AR נוצר כאובייקט חדש בעזרת שימוש באופרטור `new`, קריאה ל [save()|CActiveRecord::save] תוסיף רשומה חדשה לטבלה במסד הנתונים; במידה ואובייקט ה AR נוצר כתוצאה משימוש במתודה כמו `find` או `findAll`, קריאה ל [save()|CActiveRecord::save] תעדכן את הרשומה הקיימת בטבלה. למעשה, אנו יכולים להשתמש ב [CActiveRecord::isNewRecord] כדי להבחין במידה אובייקט ה AR הינו חדש או לא.

ניתן לעדכן רשומה אחת או יותר בטבלה במסד הנתונים ללא צורך בטעינתם מראש. AR מספקת את המתודות הבאות לנוחות השימוש:

~~~
[php]
// עדכן את הרשומות התואמות לתנאי שהועבר
Post::model()-»updateAll($attributes,$condition,$params);
// עדכן את הרשומות התואמות לתנאי שהועבר ולמפתחות הראשיים שהועברו
Post::model()-»updateByPk($pk,$attributes,$condition,$params);
// עדכן את עמודות הספירה ברשומות התואמות לתנאי שהועבר
Post::model()-»updateCounters($counters,$condition,$params);
~~~

בדוגמא למעלה, `attributes$` הינו מערך של שמות העמודות וערכיהם; `counter$` הינו מערך של שמות העמודות וערכיהם הינו מספר עולה; ו `condition$` ו `params$` כפי שהוסבר בחלק הקודם.

מחיקת רשומה
---------------

אנו יכולים למחוק רשומה במידה ואובייקט ה AR  נוצר על ידי קבלת המידע מהטבלה במסד הנתונים.

~~~
[php]
$post=Post::model()-»findByPk(10); // נניח וישנה הודעה עם המספר 10
$post-»delete(); // מחק את הרשומה מהטבלה במסד הנתונים
~~~

הערה, לאחר המחיקה, אובייקט ה AR נשאר ללא שינוי, אבל אותה שורה בטבלה במסד הנתונים נמחקה.

לנוחות ניתן להעזר במתודות הבאות כדי למחוק רשומות ללא צורך בטעינה מראש שלהם:

~~~
[php]
// מחק את הרשומות התואמות לתנאי שהועבר
Post::model()-»deleteAll($condition,$params);
// מחק את הרשומות התואמות לתנאי שהועבר ולמפתחות שהועברו
Post::model()-»deleteByPk($pk,$condition,$params);
~~~

אימות נתונים
---------------

כשמוסיפים או מעדכנים רשומה, אנו בדרך כלל צריכים לבדוק במידה והערכים שהוצבו עונים על חוקים מסויימים. זה בהחלט הכרחי במידה והערכים שמוצבים מוגדרים על ידי משתמשי קצה. בדרך כלל, אנו לא יכולים לסמוך על שום דבר המגיע מצד הלקוח.

AR מבצע אימות נתונים אוטומטית בעת הקריאה ל [()save|CActiveRecord::save]. האימות מבוסס על החוקים שהוגדרו במתודת [()rules|CModel::rules] במחלקת ה AR. למידע נוסף אודות הגדרת חוקי אימות נתונים, יש לקרוא את החלק אודות [הגדרת חוקי אימות נתונים](/doc/guide/form.model#declaring-validation-rules). למטה מוצג קוד בסיסי הנחוץ לשמירה של רשומה:

~~~
[php]
if($post-»save())
{
    // נתונים אומתו והרשומה נוספה/עודכנה
}
else
{
    // נתונים לא תקינים. יש לקרוא למתודה ה getErrors() לקבלת השגיאות שחזרו
}
~~~

כשהמידע לעדכון או הוספה של רשומה מתקבל על ידי משתמשי קצה בעזרת טופס HTML , אנו צריכים להציב אותם למאפיינים התואמים במחלקת ה AR. ניתן לבצע זאת בצורה הבאה:

~~~
[php]
$post-»title=$_POST['title'];
$post-»content=$_POST['content'];
$post-»save();
~~~

במידה וישנם עמודות רבים, אנו נראה רשימה ארוכה של הצבות כאלו. בכדי להקל על תהליך ההצבה ניתן להעזר במאפיין [attributes|CActiveRecord::attributes] כפי שמוצג בדוגמא למטה.
מידע נוסף ניתן למצוא בחלק [אבטחת הצבת מאפיינים](/doc/guide/form.model#securing-attribute-assignments) ובחלק [יצירת פעולה](/doc/guide/form.action) .

~~~
[php]
// נניח ש $_POST['Post'] הינו מערך ששמות המפתחות הינם שמות העמודות בטבלה והערכים שלהם בהתאם
$post-»attributes=$_POST['Post'];
$post-»save();
~~~


השוואת רשומות
-----------------

בדומה לשורות בטבלה, אובייקטים של AR מזוהים בצורה יחודית על ידי הערך במפתח הראשי שלהם. לכן, בכדי להשוות בין שני אובייקטים של AR, כל מה אנו צריכים לעשות זה להשוות בין את ערכי המפתחות הראשיים שלהם, בהנחה שהם שייכים לאותה מחלקת AR. למרות, שיהיה יותר קל לקרוא פשוט למתודה [()CActiveRecord::equals].

» Info|מידע: בניגוד ליישום ושימוש של AR בפריימוורקס (Frameworks) שונים אחרים, Yii תומכת במפתחות ראשיים מורכבים במחלקות ה AR שלה. מפתח ראשי מורכב מכיל שני עמודות או יותר. מפתח ראשי מורכב מיוצג על ידי מערך ב Yii, בהתאמה. המאפיין [primaryKey|CActiveRecord::primaryKey] מספק את ערך המפתח הראשי של אובייקט AR.

התאמה אישית
-------------

[CActiveRecord] מספקת כמה מתודות שניתנים לדריסה על ידי תתי המחלקות שלה כדי לשנות את רצף העבודה שלהם וההתנהלות.

- [beforeValidate|CModel::beforeValidate] ו [afterValidate|CModel::afterValidate]: מתודות אלו נקראות לפני ואחרי ביצוע אימות הנתונים.

- [beforeSave|CActiveRecord::beforeSave] ו [afterSave|CActiveRecord::afterSave]: מתודות אלו נקראות לפני ואחרי שמירת אובייקט AR.

- [beforeDelete|CActiveRecord::beforeDelete] ו [afterDelete|CActiveRecord::afterDelete]: מתודות אלו נקראות לפני ואחרי מחיקת אובייקט AR.

- [afterConstruct|CActiveRecord::afterConstruct]: מתודה זו רצה בכל פעם שאובייקט AR חדש נוצר בעזרת האופרטור `new`.

- [beforeFind|CActiveRecord::beforeFind]: מתודה זו רצה לפני שימוש באחד ממתודות ה `find` (לדוגמא `find`, `findAll`). אפשרות זו קיימת מגרסאות 1.0.9 ומעלה.

- [afterFind|CActiveRecord::afterFind]: מתודה זו רצה לאחר יצירת אובייקט AR כתוצאה מביצוע שאילתה.


שימוש בטרנזקציה בעזרת AR
-------------------------

כל אובייקט AR מכיל מאפיין בשם [dbConnection|CActiveRecord::dbConnection] אשר מייצג אובייקט של [CDbConnection]. לכן אנו יכולים להשתמש באפשרות של [טרנזקציות](/doc/guide/database.dao#using-transactions) המסופקת על ידי ה-DAO של Yii בעת הצורך במהלך השימוש ב-AR:

~~~
[php]
$model=Post::model();
$transaction=$model-»dbConnection-»beginTransaction();
try
{
    // שימוש ב find ו save הינם שני שלבים אשר ניתן להתערב בהם על ידי בקשות נוספות
    // לכן אנו משתמשים בטרנזקציה כדי לוודא המשכיות
    $post=$model-»findByPk(10);
    $post-»title='כותרת חדשה';
    $post-»save();
    $transaction-»commit();
}
catch(Exception $e)
{
    $transaction-»rollBack();
}
~~~


מרחבים מוגדרים
------------

» Note|הערה: תמיכה במרחבים מוגדרים נוספה מגרסאות 1.0.5 ומעלה. הרעיון המקורי של מרחבים מוגדרים הגיע מ  Ruby on Rails.

*מרחב מוגדר* מייצג *שם* של תנאי בשאילתה שניתן לאחד אותה ביחד עם עוד מרחבים מוגדרים ולצרף לשאילתה של AR.

מרחבים מוגדרים בדרך כלל מוגדרים במתודה [CActiveRecord::scopes] בזוגות בפורמט של שם-תנאי. הקוד הבא מגדיר שני מרחבים מוגדרים, `published` ו `recently`, במחלקה של המודל `Post`:

~~~
[php]
class Post extends CActiveRecord
{
    ......
    public function scopes()
    {
        return array(
            'published'=»array(
                'condition'=»'status=1',
            ),
            'recently'=»array(
                'order'=»'create_time DESC',
                'limit'=»5,
            ),
        );
    }
}
~~~

כל מרחב מוגדר בתור מערך שניתן לאתחל בעזרתו אובייקט של [CDbCriteria]. לדוגמא, המרחב `recently` מגדיר את המאפיין `order` בערך `create_time DESC` ואת המאפיין `limit` לערך 5, שמתורגם לתנאי בשאילתה שאמור להחזיר את חמשת ההודעות האחרונות.

שימושם העיקרי של המרחבים המוגדרים הינו *שינוי והגדרה* של קריאה למתודות `find` שונות. ניתן לשרשר כמה מרחבים מוגדרים וכתוצאה מכך לבצע שאילתה הרבה יותר מוגבלת הכוללת יותר תנאים. לדוגמא, בכדי למצוא את ההודעות שפורסמו לאחרונה, אנו יכולים להשתמש בקוד הבא:

~~~
[php]
$posts=Post::model()-»published()-»recently()-»findAll();
~~~

בדרך כלל, מרחב מוגדר צריך להיות מוגדר מצד שמאל (לבוא לפני) של המתודה `find`. כל אחד מהם מספק תנאי לשאילתה, אשר בסופו של דבר מתאחד עם תנאים אחרים, כולל את התנאי שהועבר למתודת `find`, ויוצר תנאי אחד גדול. התוצאה הסופית דומה להוספת רשימה של פילטרים לשאילתה.

החל מגרסא 1.0.6, ניתן להשתמש במרחבים מוגדרים במתודות `update` ו `delete`. לדוגמא, הקוד הבא ימחק את כל ההודעות שנוספו לאחרונה:

~~~
[php]
Post::model()-»published()-»recently()-»delete();
~~~

» Note|הערה: ניתן להשתמש במרחבים מוגדרים על מתודות במחלקה הנוכחית. זאת אומרת, המתודה צריכה להקרא על ידי `()ClassName::model`.

### מרחבים מוגדרים עם פרמטרים

ניתן להוסיף פרמטרים (סממנים) למרחבים מוגדרים. לדוגמא, אנו נרצה לשנות את הערך של ההודעות במרחב המוגדר בשם `recently`. בכדי לעשות זאת, במקום להגדיר את שם המרחב במתודה [CActiveRecord::scopes], אנו צריכים להגדיר מתודה ששמה הוא זהה לשם המרחב בו אנו משתמשים:

~~~
[php]
public function recently($limit=5)
{
    $this-»getDbCriteria()-»mergeWith(array(
        'order'=»'create_time DESC',
        'limit'=»$limit,
    ));
    return $this;
}
~~~

לאחר מכן, אנו משתמשים בביטוי הבא בכדי לקבל את שלושת ההודעות שפורסמו לאחרונה:

~~~
[php]
$posts=Post::model()-»published()-»recently(3)-»findAll();
~~~

במידה ולא נעביר את הספרה 3 כפרמטר במתודה `recently` בקוד למעלה, אנו נקבל את חמשת ההודעות שפורסמו לאחרונה כברירת מחדל.

### מרחבים מוגדרים כברירת מחדל

במחלקה של מודל ניתן להגדיר מרחב מוגדר אשר יתווסף לכל השאילתות שאנו מריצים בעזרת המחלקה (כולל קישורים לטבלאות אחרות). לדוגמא, אתר התומך בכמה שפות ירצה להציג תוכן באותה שפה שהמשתמש כרגע צופה בה. מאחר וישנם שאילתות רבות בנוגע לתוכן האתר, אנו יכולים להגדיר מרחב מוגדר ברירת מחדל בכדי לפתור בעיה זו. בכדי לבצע זאת, אנו נצטרך לדרוס את המתודה [CActiveRecord::defaultScope] בצורה הבאה,

~~~
[php]
class Content extends CActiveRecord
{
    public function defaultScope()
    {
        return array(
            'condition'=»"language='".Yii::app()-»language."'",
        );
    }
}
~~~

כעת, הביטוי הבא אוטומטית משתמש בתנאי כפי שהוגדר למעלה:

~~~
[php]
$contents=Content::model()-»findAll();
~~~

יש לזכור ששימוש במרחבים מוגדרים כברירת מחדל תקף רק לשאילתות מסוג `SELECT`. הוא אינו תקף לגבי `INSERT`, `UPDATE` ו `DELETE` ופשוט יתעלם מהם.

«div class="revision"»$Id: database.ar.txt 1681 2010-01-08 03:04:35Z qiang.xue $«/div»